use crate::integer_encoding::{FixedInt, VarInt, VarIntWriter};
use crate::key_types::ValueType;
use crate::memtable::MemTable;
use crate::types::SequenceNumber;

use std::io::Write;
/// 表示 "序列号"（Sequence Number）字段在头部中的起始偏移量为 0 字节。
/// 序列号通常用于标识记录的顺序（如日志的写入顺序、版本号），一般占用 8 字节（64 位整数），因此从 0 偏移开始，覆盖 0~7 字节。
const SEQNUM_OFFSET: usize = 0;
/// 表示 "计数"（Count）字段在头部中的起始偏移量为 8 字节。
/// 这个字段可能表示某种数量（如包含的子项数、数据条目数等），从 8 字节开始，通常占用 4 字节（32 位整数），覆盖 8~11 字节。
const COUNT_OFFSET: usize = 8;
/// 表示整个头部的总大小为 12 字节，正好是前两个字段的长度之和（8 字节 + 4 字节）。
const HEADER_SIZE: usize = 12;

/// A WriteBatch contains entries to be written to a MemTable (for example) in a compact form.
///
/// The storage format is (with the respective length in bytes)
///
/// [tag: 1, keylen: ~var, key: keylen, vallen: ~var, val: vallen]
/// WriteBatch 是一个 “批量写入容器”，它以紧凑的格式存储了一批待写入的数据条目（entries），
/// 这些条目最终会被写入到 MemTable（内存表，常见于 LevelDB/RocksDB 等存储引擎的内存缓存结构）或其他类似的存储目标中。
/**
 *
示例：一个具体的 WriteBatch 条目
假设要存储一个 “插入键值对（key: "name", value: "rust"）” 的条目，其编码过程可能如下：

tag：用 0x01 表示 “插入”（1 字节）；

keylen：key "name" 长度为 4，用 Varint 编码为 0x04（1 字节）；

key："name" 的 ASCII 编码为 0x6E 0x61 0x6D 0x65（4 字节）；

vallen：value "rust" 长度为 4，编码为 0x04（1 字节）；

val："rust" 的 ASCII 编码为 0x72 0x75 0x73 0x74（4 字节）。

最终该条目的字节流为：

0x01 0x04 0x6E 0x61 0x6D 0x65 0x04 0x72 0x75 0x73 0x74
 */
pub struct WriteBatch {
    /// Vec<u8>
    ///
    /// 存储了一批待写入的数据条目,
    ///
    /// 格式为:
    ///
    /// [tag: 1, keylen: ~var, key: keylen, vallen: ~var, val: vallen]
    entries: Vec<u8>,
}

impl WriteBatch {
    pub(crate) fn new() -> WriteBatch {
        let mut v = Vec::with_capacity(128);
        v.resize(HEADER_SIZE, 0);

        WriteBatch { entries: v }
    }

    /// Initializes a WriteBatch with a serialized WriteBatch.
    pub fn set_contents(&mut self, from: &[u8]) {
        self.entries.clear();
        self.entries.extend_from_slice(from);
    }

    /// Adds an entry to a WriteBatch, to be added to the database.
    ///
    /// 这个 put 方法用于向数据结构中写入键值对（key-value），通常用于构建 SSTable（有序字符串表）或日志文件等场景。
    /// 它按照特定的格式将键值对序列化并存储，同时维护条目数量的计数。
    #[allow(unused_assignments)]
    pub fn put(&mut self, k: &[u8], v: &[u8]) {
        // 写入一个字节的类型标识（ValueType::TypeValue），用于区分该条目是普通键值对还是删除标记（在 LSM 树中，删除操作通常会记录一个 “删除标记” 而非直接删除数据）。
        self.entries.write_all(&[ValueType::TypeValue as u8]).unwrap();
        self.entries.write_varint(k.len()).unwrap(); // 写入键的长度（变长整数格式）
        self.entries.write_all(k).unwrap();        // 写入键的原始字节
        self.entries.write_varint(v.len()).unwrap();  // 写入值的长度（变长整数格式）
        self.entries.write_all(v).unwrap();  // 写入值的长度（变长整数格式）
        // 读取当前的条目数量（self.count()）；
        // 将计数加 1 并更新（self.set_count(c + 1)），用于跟踪已写入的键值对总数。
        let c = self.count();
        self.set_count(c + 1);
    }

    /// Marks an entry to be deleted from the database.
    /// 这个 delete 方法用于在数据库中标记一个键（key）为 “已删除”，是 LSM 树存储引擎中实现删除操作的核心方式（并非直接从存储中物理删除数据，而是添加一个 “删除标记”）。
    ///
    /// delete 方法的核心功能是记录 “删除意图”，具体作用包括：
    ///
    /// 向系统标记某个键需要被删除，后续查询该键时会返回 “不存在”；
    ///
    /// 以追加写入的方式记录删除操作，保证高效的写入性能；
    ///
    /// 为后续的压缩过程提供依据，确保旧数据最终被物理清理。
    #[allow(unused_assignments)]
    pub fn delete(&mut self, k: &[u8]) {
        // 写入一个字节的类型标识（ValueType::TypeDeletion），明确标记这是一个删除操作，而非普通键值对。
        self.entries.write_all(&[ValueType::TypeDeletion as u8]).unwrap();
        // 先用变长整数（varint）编码键的长度（节省存储空间）；
        // 再写入键的实际字节内容（需要被删除的键）。
        // （注意：删除操作不需要写入值，只需记录 “要删除哪个键” 即可）
        self.entries.write_varint(k.len()).unwrap(); // 写入键的长度（变长整数格式）
        self.entries.write_all(k).unwrap(); // 写入键的原始字节
        // 读取当前的条目数量（self.count()）；
        // 将计数加 1 并更新（self.set_count(c + 1)），因为删除标记本身也是一个需要被记录的条目。
        let c = self.count();
        self.set_count(c + 1);
    }

    /// Clear the contents of a WriteBatch.
    pub fn clear(&mut self) {
        self.entries.clear()
    }

    fn byte_size(&self) -> usize {
        self.entries.len()
    }

    fn set_count(&mut self, c: u32) {
        c.encode_fixed(&mut self.entries[COUNT_OFFSET..COUNT_OFFSET + 4]);
    }

    /// Returns how many operations are in a batch.
    pub fn count(&self) -> u32 {
        u32::decode_fixed(&self.entries[COUNT_OFFSET..COUNT_OFFSET + 4])
    }

    /// 这段代码定义了一个名为 set_sequence 的方法，用于设置序列号（SequenceNumber）并将其编码存储到结构体的内部数据中。
    ///
    /// 方法接收 &mut self 和一个 s: SequenceNumber 参数，表明它会修改当前结构体实例，并设置传入的序列号
    ///
    /// 核心操作是 s.encode_fixed(&mut self.entries[SEQNUM_OFFSET..SEQNUM_OFFSET + 8])：
    ///
    /// s.encode_fixed(...) 表示将序列号 s 以固定长度编码方式进行编码
    ///
    /// &mut self.entries[SEQNUM_OFFSET..SEQNUM_OFFSET + 8] 指定了编码后的数据要写入的位置：是 self.entries 这个字节容器中从 SEQNUM_OFFSET 开始、长度为 8 字节的区间
    ///
    /// 整体操作是将编码后的序列号字节数据，写入到 self.entries 中预先定义的序列号存储位置
    fn set_sequence(&mut self, s: SequenceNumber) {
        s.encode_fixed(&mut self.entries[SEQNUM_OFFSET..SEQNUM_OFFSET + 8]);
    }

    /// self.entries[SEQNUM_OFFSET..SEQNUM_OFFSET + 8] 把self.entries这个数组中的数据从SEQNUM_OFFSET读到SEQNUM_OFFSET + 8
    /// 通过 u64::decode_fixed 方法将这 8 字节数据解析为一个 64 位无符号整数（u64），并作为 SequenceNumber 类型返回。
    pub fn sequence(&self) -> SequenceNumber {
        u64::decode_fixed(&self.entries[SEQNUM_OFFSET..SEQNUM_OFFSET + 8])
    }

    /// 这段代码定义了一个名为 iter 的公共方法，用于创建并返回 WriteBatch 类型的迭代器 WriteBatchIter。
    ///
    /// 方法 iter 接收 &self 作为参数，表明它是 WriteBatch 结构体的一个方法，用于获取当前 WriteBatch 实例的迭代器
    ///
    /// 返回值是 WriteBatchIter 类型的迭代器对象
    ///
    /// 在创建迭代器时，初始化了两个字段：
    ///
    /// batch: self：将当前 WriteBatch 实例的引用传递给迭代器
    ///
    /// ix: HEADER_SIZE：设置初始迭代索引为 HEADER_SIZE（头部大小），表明 WriteBatch 的数据结构中前 HEADER_SIZE 字节是头部信息，迭代应该从头部之后的实际条目数据开始
    pub fn iter(&self) -> WriteBatchIter {
        WriteBatchIter {
            batch: self,
            ix: HEADER_SIZE,
        }
    }

    /// 这个 insert_into_memtable 方法用于将一组键值对批量插入到内存表（MemTable）中，并为每个键值对分配唯一的序列号（Sequence number），是内存写入流程的重要环节。
    ///
    /// 接收一个初始序列号（seq）和目标内存表（mt）
    ///
    /// 通过 self.iter() 遍历当前需要插入的所有键值对（k 为键，v 为值）
    ///
    /// 对每个键值对：
    ///
    /// 如果 v 是 Some(v_)（正常值），则以 ValueType::TypeValue 类型插入内存表
    ///
    /// 如果 v 是 None（删除操作），则以 ValueType::TypeDeletion 类型插入（标记为删除）
    ///
    /// 每次插入后，序列号递增（seq += 1），确保每个操作有唯一编号
    pub fn insert_into_memtable(&self, mut seq: SequenceNumber, mt: &mut MemTable) {
        // 通过 self.iter() 遍历当前需要插入的所有键值对（k 为键，v 为值）
        for (k, v) in self.iter() {
            match v {
                // 如果 v 是 Some(v_)（正常值），则以 ValueType::TypeValue 类型插入内存表
                Some(v_) => mt.add(seq, ValueType::TypeValue, k, v_),
                // 如果 v 是 None（删除操作），则以 ValueType::TypeDeletion 类型插入（标记为删除）
                None => mt.add(seq, ValueType::TypeDeletion, k, b""),
            }
            // 每次插入后，序列号递增（seq += 1），确保每个操作有唯一编号
            seq += 1;
        }
    }

    /// 将编码后的序列号字节数据，写入到 self.entries 中预先定义的序列号存储位置
    ///
    /// 再返回当前数据
    ///
    /// 格式为:
    ///
    /// [tag: 1, keylen: ~var, key: keylen, vallen: ~var, val: vallen]
    pub fn encode(mut self, seq: SequenceNumber) -> Vec<u8> {
        self.set_sequence(seq);
        self.entries
    }
}

impl Default for WriteBatch {
    fn default() -> Self {
        Self::new()
    }
}

/// 这段代码定义了一个名为 WriteBatchIter 的公共结构体，它是 WriteBatch 类型的迭代器。
///
/// 这是一个泛型结构体，带有生命周期参数 'a，表示它持有一个对 WriteBatch 实例的引用，且该引用的生命周期与迭代器自身相关联
pub struct WriteBatchIter<'a> {
    /// 指向被迭代的 WriteBatch 实例的引用
    batch: &'a WriteBatch,
    /// 当前迭代的索引位置，用于跟踪迭代进度
    ix: usize,
}

/// The iterator also plays the role of the decoder.
///
/// 这段代码为 WriteBatchIter 结构体实现了 Rust 标准库中的 Iterator 特质，使其成为一个可迭代的对象，用于遍历 WriteBatch 中的批量操作条目。
/// 同时注释提到这个迭代器还扮演了解码器（decoder）的角色。
///
/// 核心方法 next() 这个会在调用 iter() 时自动执行：
///
/// 首先检查当前索引 ix 是否超出条目总数，超出则返回 None 表示迭代结束。
///
/// 读取当前条目的标签（tag），用于区分是插入还是删除操作。
///
/// 通过 usize::decode_var 解码出键的长度（klen），并从字节数据中提取出键（k）。
///
/// 如果标签表明是值类型（ValueType::TypeValue），则进一步解码出值的长度（vlen）并提取出值（v），返回 (k, Some(v))。
///
/// 如果是删除操作，则直接返回 (k, None)。
impl<'a> Iterator for WriteBatchIter<'a> {
    /// 定义了迭代器产生的元素类型：一个元组，包含键（&[u8]）和可选值（Option<&[u8]>）。值为 Some(v) 表示插入操作，None 表示删除操作。
    type Item = (&'a [u8], Option<&'a [u8]>);
    fn next(&mut self) -> Option<Self::Item> {
        println!("----------write batch next----------");
        // 首先检查当前索引 ix 是否超出条目总数，超出则返回 None 表示迭代结束。
        if self.ix >= self.batch.entries.len() {
            return None;
        }
        // 读取当前条目的标签（tag），用于区分是插入还是删除操作。
        let tag = self.batch.entries[self.ix];
        self.ix += 1;
        // 通过 usize::decode_var 解码出键的长度（klen），并从字节数据中提取出键（k）。
        let (klen, l) = usize::decode_var(&self.batch.entries[self.ix..])?;
        self.ix += l;
        let k = &self.batch.entries[self.ix..self.ix + klen];
        self.ix += klen;
        // 如果标签表明是值类型（ValueType::TypeValue），则进一步解码出值的长度（vlen）并提取出值（v），返回 (k, Some(v))。
        // 如果是删除操作，则直接返回 (k, None)。
        if tag == ValueType::TypeValue as u8 {
            let (vlen, m) = usize::decode_var(&self.batch.entries[self.ix..])?;
            self.ix += m;
            let v = &self.batch.entries[self.ix..self.ix + vlen];
            self.ix += vlen;
            Some((k, Some(v)))
        } else {
            Some((k, None))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::iter::Iterator;

    #[test]
    fn test_write_batch() {
        let mut b = WriteBatch::new();
        let entries: [(&[u8], &[u8]); 5] = [
            (b"abc", b"def"),
            (b"123", b"456"),
            (b"xxx", b"yyy"),
            (b"zzz", b""),
            (b"010", b""),
        ];

        for &(k, v) in entries.iter() {
            if !v.is_empty() {
                b.put(k, v);
            } else {
                b.delete(k)
            }
        }

        eprintln!("{:?}", b.entries);
        assert_eq!(b.byte_size(), 49);
        assert_eq!(b.iter().count(), 5);

        let mut i = 0;

        for (k, v) in b.iter() {
            assert_eq!(k, entries[i].0);

            match v {
                None => assert!(entries[i].1.is_empty()),
                Some(v_) => assert_eq!(v_, entries[i].1),
            }

            i += 1;
        }

        assert_eq!(i, 5);
        assert_eq!(b.encode(1).len(), 49);
    }
}
